//
// Created by vivi on 11/01/2018.
//

#ifndef HAIRCORE_HAIRSTICTIONSOLVER_H
#define HAIRCORE_HAIRSTICTIONSOLVER_H

#include "HairBaseSimulator.h"
#include <Eigen/Eigen>
#include <numeric>
#include <list>

namespace XRwy {
	class HairBaseSimulator;
	class HairBaseSimulatorConfiguration;
	class ParticleView;

	class HairStictionSolver {
	public:
		HairStictionSolver() = default;
		virtual ~HairStictionSolver() = default;

		virtual void solve(HairBaseSimulator & simulator, const HairSimulatorData & data, const Eigen::Affine3f &
			transformation, const float t) = 0;
	};

	class HairStictionCageSolver: public HairStictionSolver {
	public:

		HairStictionCageSolver(const HairBaseSimulatorConfiguration &);

		virtual ~HairStictionCageSolver() = default;
		virtual void solve(HairBaseSimulator & simulator, const HairSimulatorData & data, const Eigen::Affine3f &
		transformation, const float t);

	private:
		float stiction_rate;
		float _1_minus_stiction_rate;
	};

	struct HairStictionDensityFieldVertex {
		Eigen::Vector3f vel;
		float density;
	};

	typedef  HairStictionDensityFieldVertex *HairStictionDensityFieldVertexPointer;

	struct HairStictionDensityFieldParticlePositionInfo {
		float x, y, z; //the x, y, z index
		size_t cx, cy, cz; //cx = static_cast<size_t>(x), ...
		float rx, ry, rz; //rx = x - cx, ...

		//the interpolation weights with the order of
		//(0,0,0), (0,0,1), (0,1,0). ... (1,1,1)
		float interpolation_weights[8];

		//the sourrouding vertexes with the order of
		//(cx,cy,cz), (cx,cy,cz+1), (cx,cy+1,cz), ... (cx+1,cy+1,cz+1)
		HairStictionDensityFieldVertexPointer surrounding_vertexes[8];
	};

	class HairStictionDensityFieldSolver: public HairStictionSolver {

	public:
		HairStictionDensityFieldSolver(const HairBaseSimulatorConfiguration &);

		virtual ~HairStictionDensityFieldSolver();
		virtual void solve(HairBaseSimulator & simulator, const HairSimulatorData & data, const Eigen::Affine3f &
		transformation, const float t);

	protected:
		float stiction_rate;
		float _1_minus_stiction_rate;

		size_t cell_count;

		size_t vertex_count; //the grid node of the cell
		size_t vertex_count_2; //vertex_count ^ 2
		size_t vertex_count_3; //vertex_count ^ 3

		HairStictionDensityFieldVertex *vertexes;
		HairStictionDensityFieldParticlePositionInfo *position_infos;

		//for computing the bounding box
		float min_x, min_y, min_z, max_x, max_y, max_z, dx, dy, dz;
	};

	class HairStictionSpringSolver: public HairStictionSolver {

	public:
		HairStictionSpringSolver(const HairBaseSimulatorConfiguration &);

		virtual ~HairStictionSpringSolver();
		virtual void solve(HairBaseSimulator & simulator, const HairSimulatorData & data, const Eigen::Affine3f &
		transformation, const float t);

	protected:
		float distance; //the default distance to create a spring
		float tolerance; //a value larger than 1.0f, means how many distance we will break the spring
		float acc; //the force(acceleration speed for meters (we assume the particle has 1 unit mass)

		//4 sorting order for
		//x + y + z, x + y - z, x - y + z, x - y - z
		ParticleView **orders[4] = {nullptr, nullptr, nullptr, nullptr};

		//suppose the particle with index i and j has a spring
		//we will create a element in the list of links[i] (i < j)
		std::list<size_t> *links = nullptr;
	};
}

#endif //HAIRCORE_HAIRSTICTIONSOLVER_H
